/*
 * adc.c
 *
 * adc driver.
 *
 * Copyright (C) 2022 Microwell, Inc.
 * Subject to the GNU Public License, version 2.
 *
 * Author: guoming<guoming@microwell.net>
 */
#include "adc.h"
#include "core.h"
#include "mcu_regs.h"

#if (ADC_CHANNEL_MODE)

static int16_t adc_complement_convert(int16_t val);

void adc_init(void)
{
#if (READ_BIT(ADC_CHANNEL_MODE, AN4) == AN4)
    MODIFY_REG(ADCCON3, AN4MODE, (CONFIG_ADC_AN4_MODE << 0));
#endif

#if (READ_BIT(ADC_CHANNEL_MODE, AN5) == AN5)
    MODIFY_REG(ADCCON3, AN5MODE, (CONFIG_ADC_AN5_MODE << 1));
#endif


#if ((READ_BIT(ADC_CHANNEL_MODE, AN4) == AN4) && (READ_BIT(ADC_CHANNEL_MODE, AN5) == AN5))
    MODIFY_REG(ADCCON2, DIFFEN, (CONFIG_ADC_USING_DIFF << 0));
#endif

    /* Cal ADC Params for IC B and later version */
    if (sys_get_version() > 0) {
        MODIFY_REG(ADCCON3,  ((0x3 << 5) | VOFFSRC), ((0x3 << 5) | VOFFSRC));
        SET_BIT(ADCREG1, 0x0F);
    }
    /* Reset adc sample */
    CLEAR_REG(ADCCHEN);
    /* ADCCON1_RESET */
    CLEAR_BIT(ADCCON1, ADCCON1_RESET);

    if (CONFIG_IC_TYPE > CHIP_SS880X && IS_EV_CHIP()) {
        /* sample rate: 1M; average times: 64 */
        MODIFY_REG(ADCCON0, CLR_CLK_FILTER, ADC_1M_64);

        SET_BIT(ADCCON1, LOCKEN);   // enable sample lock
    } else {
        /* sample rate: 250K; average times: 256 */
        MODIFY_REG(ADCCON0, CLR_CLK_FILTER, ADC_250_256);

        // disable sample lock 
        CLEAR_BIT(ADCCON1, LOCKEN);  
    }

#if (GPIO_P03_NTC_MODE == NTC)
    MODIFY_REG(ADCCON1, (NTC0EN | NTC0ISET_20), (NTC0EN | NTC0ISET_20));
#endif
#if (GPIO_P01_NTC_MODE == NTC)
    MODIFY_REG(NTC1CON, (NTC1EN | NTC1ISET_18 | NTC1DST_P01), (NTC1EN | NTC1ISET_18 | NTC1DST_P01));
#elif (GPIO_P16_NTC_MODE == NTC)
    MODIFY_REG(NTC1CON, (NTC1EN | NTC1ISET_18 | NTC1DST_P16), (NTC1EN | NTC1ISET_18 | NTC1DST_P16));
#endif
    #if 0
    /* single mode */
    SET_BIT(ADCCON1, ADCEN);    //interrupt en
    /* enable ADC */
    WRITE_REG(ADCCHEN, ADC_CHANNEL_MODE);
    SET_BIT(ADCCON0, ADC_START_SINGLE);    //start single mode
    CLEAR_BIT(ADCCON1, ADCIF);
    #else
    /* continue mode */
    CLEAR_BIT(ADCCON0, ADC_MODE);

    WRITE_REG(ADCCHEN, ADC_CHANNEL_MODE);
    sys_delay_ms(200);
    #endif
}

void adc_resume(void)
{
    /** TODO: **/
}

void adc_exit(void)
{
    /** TODO: **/
}

/* get vbat in mv */
int16_t adc_get_vbat(void)
{
    uint16_t vbat_mv;

    /* NOTE: L must read first in case sample lock is enabled */
    vbat_mv = ADCDAT0L;
    vbat_mv += ((ADCDAT01H & 0x0F) << 8);

    return (adc_complement_convert(vbat_mv) + 2600);
}

/* get ibat (raw value), real_ibat(mA) = ibat_raw * 0.4564  */
int16_t adc_get_ibat_chg_raw(void)
{
    uint16_t ibat_raw;

    if (CONFIG_IC_TYPE > CHIP_SS880X && IS_EV_CHIP())
        ibat_raw = ADCDAT0L;

    /* NOTE: L must read first in case sample lock is enabled */
    ibat_raw = ADCDAT1L;
    ibat_raw += ((ADCDAT01H & 0xF0) << 4);

    if (READ_BIT(CHGCON3, IMOD))
        return adc_complement_convert(ibat_raw);
    else
        return (adc_complement_convert(ibat_raw) >> 2);
}

/* get AN2 in mv */
int16_t adc_get_an2(void)
{
    uint16_t val;
    
    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT2L;
    val += ((ADCDAT23H & 0x0F) << 8);

    return (adc_complement_convert(val) + 1500 - adc_get_offset());
}

/* get AN3 in mv */
int16_t adc_get_an3(void)
{
    uint16_t val;

    if (CONFIG_IC_TYPE > CHIP_SS880X && IS_EV_CHIP())
        val = ADCDAT2L;

    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT3L;
    val += ((ADCDAT23H & 0xF0) << 4);

    return (adc_complement_convert(val) + 1500 - adc_get_offset());
}

/*
 * get AN4:
 * if CONFIG_ADC_USING_DIFF == 1 or CONFIG_ADC_AN4_MODE == 0, unit is 36*mv
 * if CONFIG_ADC_USING_DIFF == 0 and CONFIG_ADC_AN4_MODE == 1, unit is mv
 */
int16_t adc_get_an4(void)
{
    uint16_t val;
    
    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT4L;
    val += ((ADCDAT45H & 0x0F) << 8);

#if (CONFIG_ADC_USING_DIFF == 1 || CONFIG_ADC_AN4_MODE == 0)
    return adc_complement_convert(val);
#else
    return (adc_complement_convert(val) + 1500 - adc_get_offset());
#endif
}

/*
 * get AN5:
 * if CONFIG_ADC_USING_DIFF == 1 or CONFIG_ADC_AN5_MODE == 0, unit is 36mv
 * if CONFIG_ADC_USING_DIFF == 0 and CONFIG_ADC_AN5_MODE == 1, unit is mv
 */
int16_t adc_get_an5(void)
{
    uint16_t val;

    if (CONFIG_IC_TYPE > CHIP_SS880X && IS_EV_CHIP())
        val = ADCDAT4L;

    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT5L;
    val += ((ADCDAT45H & 0xF0) << 4);

#if (CONFIG_ADC_USING_DIFF == 1 || CONFIG_ADC_AN5_MODE == 0)
    return adc_complement_convert(val);
#else
    return (adc_complement_convert(val) + 1500 - adc_get_offset());
#endif
}

/* get AN6 in mv */
int16_t adc_get_an6(void)
{
    uint16_t val;

    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT6L;
    val += ((ADCDAT67H & 0x0F) << 8);
    
    return adc_complement_convert(val) + 1500;
}

/* get AN7 in mv */
int16_t adc_get_an7(void)
{
    uint16_t val;
  
    if (CONFIG_IC_TYPE > CHIP_SS880X && IS_EV_CHIP())
        val = ADCDAT6L;

    /* NOTE: L must read first in case sample lock is enabled */
    val = ADCDAT7L;
    val += ((ADCDAT67H & 0xF0) << 4);

    return (adc_complement_convert(val) + 1500 - adc_get_offset());
}

int8_t adc_get_offset(void)
{
    int16_t val = adc_get_an6();
    
    /* versions A can't use adc offset */
    if (sys_get_version() == 0)
        return 0;
    
    if (val > 100 || val < -100)
        return 0;
    else
        return val;
}

static int16_t adc_complement_convert(int16_t val)
{
    val = val << 4;
    val = val >> 4;

    return val;
}
#else
void adc_init(void) {}
void adc_resume(void) {}
void adc_exit(void) {}
int16_t adc_get_vbat(void) { return 0; }
int16_t adc_get_ibat_chg_raw(void) { return 0; }
int16_t adc_get_an2(void) { return 0; }
int16_t adc_get_an3(void) { return 0; }
int16_t adc_get_an4(void) { return 0; }
int16_t adc_get_an5(void) { return 0; }
int16_t adc_get_an6(void) { return 0; }
int16_t adc_get_an7(void) { return 0; }
int8_t adc_get_offset(void) { return 0; }
#endif
